﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.Drawing.Imaging;
using System.Text.RegularExpressions;


namespace ImageProcessing
{
	public partial class SpecialEffectsDialog : Form
	{
		Bitmap bmIn;
		Bitmap bmOut;
		Bitmap bmModTh;
		Bitmap bmBaseTh;
		const int THUMBNAILSIZE = 300;
		string[] asEffects = { "Pixelation", "Ripple", "Twirl", "Jitter", "CircularSlice" };
		bool imageReady;
		bool isChanged = false;
		bool isSpecialEffect = false;
		double[,,] adImage;
		double[, ,] adImageTh;
		double[, ,] adImageModTh;

		delegate double[,,] delMorphological(double[, ,] adImage, double[,] adB, int nImageHeight, int nImageWidth, int nBWidth, int nBHeight);
		delegate int somerandomjunk(int a);
		Dictionary<string, delMorphological> dictOperations;
		Dictionary<string, string[]> dictFilters;
		delMorphological morphOperation;


		#region Properties

		public Bitmap ImageIn
		{
			get { return bmIn; }
			set
			{
				bmIn = new Bitmap(value);
				//size thumbnail picboxes to proper size
				picBoxBase.Size = new Size(THUMBNAILSIZE, THUMBNAILSIZE);
				picBoxModified.Size = new Size(THUMBNAILSIZE, THUMBNAILSIZE);
				bmBaseTh = (Bitmap)value.GetThumbnailImage(THUMBNAILSIZE, THUMBNAILSIZE, null, IntPtr.Zero);
				bmModTh = new Bitmap(bmBaseTh);

				//normalize image for modifications
				this.Cursor = Cursors.WaitCursor;
				adImage= BitmapConversions.NormalizeRGB(bmIn);
				adImageTh= BitmapConversions.NormalizeRGB(bmBaseTh);
				this.Cursor = Cursors.Default;
				imageReady = true;
			}
		}

		public Bitmap ImageOut
		{
			get { return bmOut; }
			set { bmOut = new Bitmap(value); }
		}
		#endregion Properties

		#region Constructors

		public SpecialEffectsDialog()
		{
			imageReady = false;
			InitializeComponent();
		}

		public SpecialEffectsDialog(Bitmap bmin)
		{
			InitializeComponent();

			ImageIn = bmin;
			fillMorphologyComboBox();
			fillMorphFilterComboBox();
			txtboxFilter.Lines = new string[3] { "-1 -1 1", "-1 1 -1", "1 -1 -1" };
		
			imageReady = true;
		}


		#endregion Constructors

		#region Click Events


		private void btnDoEffect_Click(object sender, EventArgs e)
		{
			bmModTh = DoEffect(bmBaseTh, chkboxPolar.Checked);
			if (bmModTh == null)
			{
				bmModTh = new Bitmap(bmBaseTh);
			}
			isSpecialEffect = true;
			picBoxModified.Invalidate();
		}

		private void btnOk_Click(object sender, EventArgs e)
		{
			if (isChanged)
			{
				if (isSpecialEffect)
					ImageOut = DoEffect(bmIn, chkboxPolar.Checked);
				else
					ImageOut = DoMorph();
			}//end if
		}

		private void btnMorphology_Click(object sender, EventArgs e)
		{
			int nWidth = 0, nHeight = 0;
			double[,] adFilter = getFilter(ref nWidth, ref nHeight);
			int nNumOps = 1;

			if (cmbboxMorph.Text == "Gradient")
			{
				try
				{
					nNumOps = Convert.ToInt32(txtboxP1.Text);
					lblError1.Visible = false;
				} //end try
				catch 
				{
					txtboxP1.Text = "1";
					nNumOps = 1;
					lblError1.Visible = true;
				} //end catch
				adImageModTh = Morphology.Gradient(adImageTh, adFilter, THUMBNAILSIZE, THUMBNAILSIZE, nWidth, nHeight,nNumOps);
			}//end if
			else
			{
				adImageModTh = dictOperations[cmbboxMorph.Text](adImageTh, adFilter, THUMBNAILSIZE, THUMBNAILSIZE, nWidth, nHeight);
			} //end else
			bmModTh = BitmapConversions.UnNormalizeRGB(adImageModTh, THUMBNAILSIZE, THUMBNAILSIZE, chkboxClamp.Checked, bmIn.PixelFormat);
			isSpecialEffect = false;
			isChanged = true;
			picBoxModified.Invalidate();
			

		}

		#endregion Click Events

		#region Do Operations
		private Bitmap DoMorph()
		{
			Bitmap bmChanged;
			int nWidth = 0, nHeight = 0;
			double[,] adFilter = getFilter(ref nWidth, ref nHeight);

			adImage = dictOperations[cmbboxMorph.Text](adImage, adFilter, bmIn.Width, bmIn.Height, nWidth, nHeight);
			bmChanged = BitmapConversions.UnNormalizeRGB(adImage, bmIn.Width, bmIn.Height, true, bmIn.PixelFormat);
			return bmChanged;
		}

		private Bitmap DoEffect(Bitmap bmChanging, bool doPolar)
		{
			Bitmap bmChanged;
			int p1, p2;
			try
			{
				p1 = Int32.Parse(txtboxP1.Text);
				p2 = Int32.Parse(txtboxP2.Text);

			}
			catch
			{
				txtboxP1.Text = "6";
				txtboxP2.Text = "6";
				lblError1.Visible = true;
				return null;
			}
			lblError1.Visible = false;

			switch (cmbboxEffects.Text)
			{
				case "Pixelation":
					bmChanged = SpecialEffects.Pixelation(bmChanging, p1, p2, doPolar);
					isChanged = true;
					break;
				case "Ripple":
					bmChanged = SpecialEffects.Ripple(bmChanging, p1, p2, doPolar);
					isChanged = true;
					break;
				case "Twirl":
					bmChanged = SpecialEffects.Twirl(bmChanging, p1);
					isChanged = true;
					break;
				case "Jitter":
					bmChanged = SpecialEffects.Jitter(bmChanging, p1, p2);
					isChanged = true;
					break;
				case "CircularSlice":
					bmChanged = SpecialEffects.CircularSlice(bmChanging, p1);
					isChanged = true;
					break;
				default:
					bmChanged = new Bitmap(bmChanging);
					break;
			} //end switch	

			return bmChanged;
		}
		#endregion

		#region OnPaint
		private void picBoxModified_Paint(object sender, PaintEventArgs e)
		{
			if (imageReady)
				picBoxModified.BackgroundImage = bmModTh;
		}

		private void picBoxBase_Paint(object sender, PaintEventArgs e)
		{
			if (imageReady)
				picBoxBase.BackgroundImage = bmBaseTh;
		}
		#endregion OnPaint



		#region helpers
		
		private double[,] getFilter(ref int nWidth, ref int nHeight)
		{
			string strPattern = @"-?\d+\s*";
			MatchCollection matches;
			int nRow = 0;
			int nCol = 0;
			double[,] adFilter = new double[1, 1];

			foreach (string line in txtboxFilter.Lines)
			{
				matches = Regex.Matches(line, strPattern);
				//if first one, get width and length of filter
				if (nRow == 0)
				{
					nWidth = matches.Count;
					nHeight = txtboxFilter.Lines.Count();
					adFilter = new double[nWidth, nHeight];
				} //end if
				nCol = 0;
				foreach (Match m in matches)
				{
					try
					{
						//normalize between 0..1 from 0..255
						adFilter[nRow, nCol] = Convert.ToDouble(m.Value)/255.0;
					} //end try
					catch
					{
						//if can't convert value, set to -1 (ignore
						adFilter[nRow, nCol] = -1;
					} //end catch
					nCol++;

					//if too many values, skip the extras
					if (nCol == nHeight)
						break;
				} //end foreach

				//if not enough values provided, set rest to 0
				if (nCol != nHeight - 1)
				{
					for (int i = nCol; i < nHeight; i++)
					{
						adFilter[nRow, nCol] = -1;
					} //end for
				} //end if

				nRow++;
			} //end foreach	

			return adFilter;
		}//end getFilter

		private void fillMorphologyComboBox()
		{
			dictOperations = new Dictionary<string, delMorphological>();

			morphOperation = Morphology.Dilate;
			dictOperations.Add("Dilate", morphOperation);
			morphOperation = Morphology.Erode;
			dictOperations.Add("Erode", morphOperation);
			morphOperation = Morphology.Open;
			dictOperations.Add("Open", morphOperation);
			morphOperation = Morphology.Close;
			dictOperations.Add("Close", morphOperation);
			morphOperation = Morphology.InsideContour;
			dictOperations.Add("InsideContour", morphOperation);
			morphOperation = Morphology.OutsideContour;
			dictOperations.Add("OutsideContour", morphOperation);
			morphOperation = Morphology.MiddleContour;
			dictOperations.Add("MiddleContour", morphOperation);
			morphOperation = Morphology.TopHat1;
			dictOperations.Add("TopHat1", morphOperation);
			morphOperation = Morphology.TopHat2;
			dictOperations.Add("TopHat2", morphOperation);
			//Hack: Gradient is special case, so attaching TopHat2 to it won't fire, we'll handle it as a special case
			dictOperations.Add("Gradient", morphOperation);

			KeyValuePair<string, delMorphological>[] lstValues = dictOperations.ToArray();
			cmbboxMorph.DataSource = lstValues;
			cmbboxMorph.DisplayMember = "Key";
			cmbboxMorph.ValueMember = "Value";
		}

		private void fillMorphFilterComboBox()
		{
			string[] asFilter;
			dictFilters = new Dictionary<string, string[]>();
			asFilter = new string[]
			{
				"-1 -1 1",
				 "-1 1 -1",
				 "1 -1 -1"
			};
			dictFilters.Add("3x3Line", asFilter);


			asFilter = new string[]
			{
				"-1 -1 -1 -1 -1 -1 1",
				"-1 -1 -1 -1 -1 1 -1",
				"-1 -1 -1 -1 1 -1 -1",
				"-1 -1 -1 1 -1 -1 -1",
				"-1 -1 1 -1 -1 -1 -1",
				"-1 1 -1 -1 -1 -1 -1",
				"1 -1 -1 -1 -1 -1 -1",
			};
			dictFilters.Add("7x7Line", asFilter);

			asFilter = new string[]
			{
				"-1 -1 -1 -1 1 -1 -1 -1 -1",
				"-1 -1 -1 1 1 1 -1 -1 -1",
				"-1 -1 1 1 1 1 1 -1 -1",
				"-1 1 1 1 1 1 1 1 -1",
				"1 1 1 1 1 1 1 1 1",
				"-1 1 1 1 1 1 1 1 -1",
				"-1 -1 1 1 1 1 1 -1 -1",
				"-1 -1 -1 1 1 1 -1 -1 -1",
				"-1 -1 -1 -1 1 -1 -1 -1 -1",
			};
			dictFilters.Add("7x7Diamond", asFilter);
			asFilter = new string[]
			{
				"-1 -1 -1 -1 1 -1 -1 -1 -1",
				"-1 -1 1 1 1 1 1 -1 -1",
				"-1 1 1 1 1 1 1 1 -1",
				"-1 1 1 1 1 1 1 1 -1",
				"1 1 1 1 1 1 1 1 1",
				"-1 1 1 1 1 1 1 1 -1",
				"-1 1 1 1 1 1 1 1 -1",
				"-1 -1 1 1 1 1 1 -1 -1",
				"-1 -1 -1 -1 1 -1 -1 -1 -1",
			};
			dictFilters.Add("7x7Dish", asFilter);
			asFilter = new string[]
			{
				"1 1 1 1 1 1 1 1 1",
				"-1 -1 1 1 1 1 1 -1 -1",
				"-1 -1 1 1 1 1 1 -1 -1",
				"-1 -1 1 1 1 1 1 -1 -1",
				"1 1 1 1 1 1 1 1 1",
			};
			dictFilters.Add("5x9Rectangle", asFilter);

			KeyValuePair<string, string[]>[] filterKeys = dictFilters.ToArray();
			cmbboxFilters.DataSource = filterKeys;
			cmbboxFilters.DisplayMember = "Key";

		}

		#endregion helpers

		private void cmbboxFilters_SelectedIndexChanged(object sender, EventArgs e)
		{
			txtboxFilter.Clear();
			txtboxFilter.Lines = dictFilters[cmbboxFilters.Text];
		}
	}
}
